#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>

#define MAXLINE 40

void str_echo(int sockfd)
{
	ssize_t n;
	char buf[MAXLINE];

again:
	while((n = recv(sockfd, buf, MAXLINE,0)) > 0){
		printf("recv msg from client: %s\n", buf);
		if( send(sockfd, buf, strlen(buf), 0) < 0){
			printf("send msg error: %s(errno: %d)\n", 
					strerror(errno), errno);
			 exit(0);
		}
	}


	if(n < 0 && errno == EINTR)
		goto again;
	else if (n < 0)
		printf("str_echo : read error");
}

void do_select(int listenfd)
{
	int connfd,sockfd;
	struct sockaddr_in cliaddr;
	socklen_t cliaddrlen;
	fd_set rset,allset;
	int maxfd, maxi = -1;
	int i = 0, n;
	int clientfds[FD_SETSIZE];
	int nready;

	char buf[MAXLINE];
	memset(buf, 0, MAXLINE);

	for (i = 0; i < FD_SETSIZE; i++)
		clientfds[i] = -1;
	maxi = -1;
	FD_ZERO(&allset);
	//添加监听描述符
	FD_SET(listenfd,&allset);
	maxfd = listenfd;



	int *num,read_n = 0;
	int offset = 4;

	//循环处理
	while(1){

		rset = allset;
		//获取可用描述符的个数
		nready = select(maxfd+1,&rset,NULL,NULL,NULL);
		if (nready == -1){
			 perror("select error:");
			 exit(1);
		 }
#if 1
		//测试监听描述符是否准备好
		if (FD_ISSET(listenfd,&rset))
		{
#if 1
			cliaddrlen = sizeof(cliaddr);
			//接受新的连接
			if ((connfd = accept(listenfd,(struct sockaddr*)&cliaddr,&cliaddrlen)) == -1) {
				if (errno == EINTR)
					continue;
				else{
					perror("accept error:");
					exit(1);
				}
			}
			//将新的连接描述符添加到数组中
			for (i = 0;i <FD_SETSIZE;i++){
				if (clientfds[i] < 0){
					clientfds[i] = connfd;
					break;
				}
			}
			if (i == FD_SETSIZE){
				fprintf(stderr,"too many clients.\n");
				exit(1);
			}
			//将新的描述符添加到读描述符集合中
			FD_SET(connfd,&allset);
			//描述符个数
			maxfd = (connfd > maxfd ? connfd : maxfd);
			//记录客户连接套接字的个数
			maxi = (i > maxi ? i : maxi);
			if (--nready <= 0)
				continue;
#endif
		}
#endif
		//处理客户连接
#if 1
		for(i = 0; i <= maxi; i++){
			if((sockfd = clientfds[i]) < 0)	continue;
			if(FD_ISSET(sockfd, &rset)){
				if((n = read(sockfd, buf,MAXLINE)) == 0){
					close(sockfd);
					FD_CLR(sockfd, &allset);
					clientfds[i] = -1;
				}
//				printf("get message from client: %s\n", buf + 4);
//				memset(buf, 0, MAXLINE);
				num = (int *)buf;
				if(num <= 0) {
					printf("get no message\n");
					continue;
				}
#if 1
				read_n = 0;
				if(*num < n) {
					printf("get too many message at one time,lost message\n");
					printf("first message: %s\n", buf + 4);
				} else if (*num == n) { 
					printf("get message from client: %s\n", buf + 4);
					if((n = send(sockfd, buf, MAXLINE - 4, 0)) == 0)
							printf("send no msg/n");
				} else if (*num > n) {
					printf("get long meaasge from client: %s", buf + 4);
					read_n = n - 4;
					while ((*num - 4) > read_n){
						if((n = read(sockfd, buf,MAXLINE)) == 0){
							close(sockfd);
							FD_CLR(sockfd, &allset);
							clientfds[i] = -1;
							break;
						}
						printf("%s", buf);
						read_n = read_n + n;
					}
					printf("\n");
				}
				memset(buf, 0, MAXLINE);
#endif

				if (--nready <= 0)
					break;
			}
		}
#endif

	}
}


int main()
{
	int listenfd;
	struct sockaddr_in servaddr;

	if((listenfd = socket(AF_INET, SOCK_STREAM, 0)) == -1) {
		printf("creat socket error: %s(errno: %d)\n", strerror(errno), errno);
	}

	memset(&servaddr, 0, sizeof(servaddr));
	servaddr.sin_family = AF_INET;	//协议簇，这里选择了ipv4
	servaddr.sin_addr.s_addr = htonl(INADDR_ANY);//对IPV4设置通配地址
	servaddr.sin_port = htons(6667);//设置端口号666

	if(bind(listenfd, (struct sockaddr*)&servaddr, sizeof(servaddr)) == -1){
		printf("bind socket error: %s(errno: %d)\n",strerror(errno),errno);
    		exit(0);       		
	}

	if( listen(listenfd, 10) == -1){ //socket可以排队的最大连接个数10个
		printf("listen socket error: %s(errno: %d)\n",strerror(errno),errno);
		exit(0);
	}
#if 0

	printf("========waiting for client's request=========\n");
	while(1){
		//第二个参数为指向struct sockaddr *的指针，用于返回客户端的协议地址
		//第三个参数为协议地址的长度
		clilen = sizeof(cliaddr);
		if((connfd = accept(listenfd, 
				(struct sockaddr*)&cliaddr, &clilen)) == -1){
			printf("accept socket error: %s(errno: %d)",
						strerror(errno),errno);
			continue;
		}

		if((childpid = fork()) == 0) {
			close(listenfd);
			str_echo(connfd);
			exit(0);
		}
		close(connfd);
	}
#endif
	printf("========waiting for client's request=========\n");
	do_select(listenfd);

	return 0;
}
